/**
 * @file AccountCommandImpl.java
 * Projet eCOM 2010 (Miage Descartes)
 * @author Doido Sébastien
 * @version 2
 * @brief Shell : Gestion des fonctionnalités relatives aux comptes acheteurs/magasins
 */
package ecom.shell;

import ecom.beans.AccountBean;
import ecom.beans.EcomAdminRemote;
import ecom.beans.EcomCustomerRemote;
import ecom.beans.UnknownIdentifier;
import ecom.shell.OutputCommandImpl.Outputs;
import ecom.shell.ShellConstantes.RESULT_ACTION;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.StringTokenizer;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

/**
 * @brief Shell : Gestion des fonctionnalités relatives aux comptes acheteurs/magasins
 * @author Sebastien
 */
public class AccountCommandImpl implements ShellCommand {

    private ShellContext _context = null;

    public AccountCommandImpl(ShellContext context) {
        _context = context;
    }

    /**
     * @brief Renvoie le nom de la commande
     * @return le nom de la commande
     */
    public String getName() {
        return "account";
    }

    /**
     * @brief Renvoie la manière d'utiliser la commande
     * @return la manière d'utiliser la commande
     */
    public String getUsage() {
        return "account";
    }

    /**
     * @brief Renvoie la description de la commande
     * @return la description de la commande
     */
    public String getShortDescription() {
        String description = "Account management\n";
        if (true == _context.getVar("adminMode")) {
            description += "\t  : -add [name] [balance] [login] [pass] [surname] [address] [birthdate]...create an account\n";
            description += "\t  : -remove [account ID]...................................................delete an account\n";
        } else {
            description += "\t  : -deposit [account ID] [amount].........................................deposit cash on account ID\n";
        }
        description += "\t  : -id [account ID].......................................................display specified account details\n";
        return description;
    }

    /**
     * @brief Exécute la commande
     * @param cmdline : commande à exécuter
     * @param out : flux d'affichage std
     * @param err : flux d'affichage err
     * @return les données à afficher
     * @throws java.lang.Exception : erreur durant l'exécution de la méthode
     */
    public String execute(String cmdline, PrintStream out, PrintStream err) throws InsuffisantPrivilege {
        StringBuffer res = new StringBuffer();
        EcomCustomerRemote ecomCustomerBean = (EcomCustomerRemote) _context.getVar("customerBean");
        EcomAdminRemote ecomAdminBean = (EcomAdminRemote) _context.getVar("adminBean");
        Outputs currentOutput = (Outputs) _context.getVar("output");
        String currentCurrency = String.valueOf(_context.getVar("currency"));

        Collection<AccountBean> accountList = null;

        StringTokenizer st = new StringTokenizer(cmdline, " ");

        // Ignore cmd
        st.nextToken();

        // without arg : list all accounts
        if (st.countTokens() == 0) {
            if (true != _context.getVar("adminMode")) {
                throw new InsuffisantPrivilege();
            }
            accountList = ecomAdminBean.getAllAccounts();
            return printList(ecomCustomerBean, currentCurrency, out, accountList, currentOutput);
        } // With option
        else {
            String option = st.nextToken();

            // adding
            if (option.equals("-add")) {
                if (true != _context.getVar("adminMode")) {
                    throw new InsuffisantPrivilege();
                }
                if (st.countTokens() == 7) {
                    try {
                        String accountOwner;
                        double balance;
                        String login, pass, surname, address, birthdate;

                        accountOwner = st.nextToken();
                        balance = Double.valueOf(st.nextToken());
                        login = st.nextToken();
                        pass = st.nextToken();
                        surname = st.nextToken();
                        address = st.nextToken();
                        birthdate = st.nextToken();

                        int id = ecomAdminBean.createAccount(accountOwner, balance, login, pass, surname, address, birthdate);
                        res.append(_context.returnCode("Account Created with id #" + id, RESULT_ACTION.SUCCESS));
                    } catch (Exception e) {
                        res.append(_context.returnCode("Error with account creation", RESULT_ACTION.FAILED));
                        err.println("Error with account creation");
                    }
                } else {
                    res.append(_context.returnCode(Shell.INCORRECT_ARGS_NUMBER, RESULT_ACTION.FAILED));
                    err.println(Shell.INCORRECT_ARGS_NUMBER);
                }
            } // removing
            else if (option.equals("-remove")) {
                if (true != _context.getVar("adminMode")) {
                    throw new InsuffisantPrivilege();
                }
                if (st.countTokens() == 1) {
                    try {
                        int id = Integer.valueOf(st.nextToken());
                        ecomAdminBean.removeAccount(id);
                        res.append(_context.returnCode("Account #" + id + " deleted", RESULT_ACTION.SUCCESS));
                    } catch (UnknownIdentifier ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println(ex);
                    } catch (Exception ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println("Error with account remove" + ex);
                    }
                } else {
                    err.println(Shell.INCORRECT_ARGS_NUMBER);
                }
            } else if (option.equals("-id")) {
                if (st.countTokens() == 1) {
                    try {
                        int id = Integer.valueOf(st.nextToken());
                        AccountBean a = ecomCustomerBean.getAccountById(id);
                        return printAccount(ecomCustomerBean, currentCurrency, out, a, currentOutput);
                    } catch (UnknownIdentifier ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println(ex);
                    } catch (Exception ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println("Error with account detail");
                    }
                } else {
                    err.println(Shell.INCORRECT_ARGS_NUMBER);
                }
            } else if (option.equals("-deposit")) {
                if (st.countTokens() == 2) {
                    try {
                        int id = Integer.valueOf(st.nextToken());
                        double amount = Double.valueOf(st.nextToken());
                        ecomCustomerBean.deposit(id, amount);
                        res.append(_context.returnCode("DEPOSIT OK", RESULT_ACTION.SUCCESS));
                    } catch (UnknownIdentifier ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println(ex);
                    } catch (Exception ex) {
                        res.append(_context.returnCode(ex.toString(), RESULT_ACTION.FAILED));
                        err.println("Error with account detail");
                    }
                } else {
                    res.append(_context.returnCode(Shell.INCORRECT_ARGS_NUMBER, RESULT_ACTION.FAILED));
                    err.println(Shell.INCORRECT_ARGS_NUMBER);
                }
            } else {
                res.append(_context.returnCode(option + " is not known for Account Management", RESULT_ACTION.FAILED));
                err.println("\t" + option + " is not known for Account Management");
            }
        }
        return res.toString();
    }

    /**
     * @brief Affiche la liste des comptes
     * @param ecomCustomerBean : bean remote client
     * @param currentCurrency : devise active
     * @param out : flux d'affichage
     * @param accountList : liste des comptes
     * @param currentOutput : type de sortie attendue
     * @return les données à afficher selon le type de sortie positionné
     */
    private String printList(EcomCustomerRemote ecomCustomerBean, String currentCurrency, PrintStream out, Collection<AccountBean> accountList, Outputs currentOutput) {
        StringBuffer res = new StringBuffer();
        if (currentOutput.equals(Outputs.XML)) {
            Element accounts = new Element("accounts");
            org.jdom.Document document = new Document(accounts);

            for (AccountBean a : accountList) {
                accounts.addContent(buildXMLNode(a, ecomCustomerBean, currentCurrency));
            }
            XMLOutputter sortie = new XMLOutputter(Format.getPrettyFormat());
            res.append(sortie.outputString(document));
        } else if (currentOutput.equals(Outputs.TEXT)) {
            res.append("#################" + "\n");
            res.append("# Known account #" + "\n");
            res.append("#################" + "\n");
            for (AccountBean a : accountList) {
                res.append(printTEXT(out, a, ecomCustomerBean, currentCurrency) + "\n");
            }
        } else {
            res.append("\t /!\\ Output format '" + currentOutput + "' is known but not implementing for this command");
        }
        return res.toString();
    }

    /**
     * @brief Affiche un compte
     * @param ecomCustomerBean : bean remote client
     * @param currentCurrency : devise active
     * @param out : flux d'affichage
     * @param a : compte
     * @param currentOutput : type de sortie attendue
     * @return les données à afficher selon le type de sortie positionné
     */
    private String printAccount(EcomCustomerRemote ecomCustomerBean, String currentCurrency, PrintStream out, AccountBean a, Outputs currentOutput) {
        Collection<AccountBean> list = new ArrayList();
        list.add(a);
        return printList(ecomCustomerBean, currentCurrency, out, list, currentOutput);
    }

    /**
     * @brief Affiche un compte en mode TEXT
     * @param out : flux d'affichage
     * @param a : compte
     * @param ecomCustomerBean : bean remote client
     * @param currentCurrency : devise active
     * @return les données à afficher en mode TEXT
     */
    private String printTEXT(PrintStream out, AccountBean a, EcomCustomerRemote ecomCustomerBean, String currentCurrency) {
        DecimalFormat myFormatter = new DecimalFormat("##0.00");
        return ("\t" + a.getAccountId() + " | " + a.getAccountOwner() + " " + a.getSurname() + " [" + a.getLogin() + "/" + a.getPass() + "] @'" + a.getAddress() + "' [balance: " + myFormatter.format(ecomCustomerBean.convertFromEuro(a.getBalance(), currentCurrency)) + currentCurrency + "]");
    }

    /**
     * @brief Construit l'arborescence XML pour un compte
     * @param a : compte
     * @param ecomCustomerBean : bean remote client
     * @param currentCurrency : devise active
     * @return le noeud account XML
     */
    private Element buildXMLNode(AccountBean a, EcomCustomerRemote ecomCustomerBean, String currentCurrency) {
        Element account = new Element("account");
        Attribute id = new Attribute("id", String.valueOf(a.getAccountId()));
        account.setAttribute(id);

        Element login = new Element("login");
        login.setText(a.getLogin());
        account.addContent(login);

        Element pass = new Element("pass");
        pass.setText(a.getPass());
        account.addContent(pass);

        Element name = new Element("name");
        name.setText(a.getAccountOwner());
        account.addContent(name);

        Element surname = new Element("surname");
        surname.setText(a.getSurname());
        account.addContent(surname);

        Element address = new Element("address");
        address.setText(a.getAddress());
        account.addContent(address);

        Element birthdate = new Element("birthdate");
        birthdate.setText(a.getBirthdate());
        account.addContent(birthdate);

        Element balance = new Element("balance");
        DecimalFormat myFormatter = new DecimalFormat("##0.00");
        balance.setText(myFormatter.format(ecomCustomerBean.convertFromEuro(a.getBalance(), currentCurrency)));
        Attribute currency = new Attribute("currency", currentCurrency);
        balance.setAttribute(currency);
        account.addContent(balance);

        return account;
    }
}
